package com.neusoft.bizcore.activiti.service.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.neusoft.bizcore.activiti.constants.BizcoreActivitiConstants;
import com.neusoft.bizcore.activiti.converter.ProcessNodeConverter;
import com.neusoft.bizcore.activiti.dto.ProcessBpmnDTO;
import com.neusoft.bizcore.activiti.dto.ProcessNodeDTO;
import com.neusoft.bizcore.activiti.service.MoldDefinitionService;
import com.neusoft.bizcore.web.exception.UnitedException;

import io.micrometer.core.instrument.util.IOUtils;

@Service
public class MoldDefinitionServiceImpl implements MoldDefinitionService {

    @Autowired
    private ProcessNodeConverter processNodeConverter;
    @Autowired
    private RepositoryService repositoryService;

    @Override
    public void create(final ProcessBpmnDTO dto) {

        try {
            // 创建一个部署构造器
            final DeploymentBuilder builder = this.repositoryService.createDeployment();
            // 流程名称
            builder.name(dto.getName());
            // 资源名称和具体
            builder.addString(dto.getName(), dto.getXmlContent());
            // 分类
            builder.category(dto.getCategory());
            // 部署
            builder.deploy();
        } catch (final Exception e) {
            System.out.println(e.toString());
        }
    }

    @Override
    public void delete(final String processKey) {

        final List<Deployment> deploymentList =
                this.repositoryService.createDeploymentQuery().processDefinitionKey(processKey).list();
        for (final Deployment deployment : deploymentList) {
            try {
                this.repositoryService.deleteDeployment(deployment.getId(), true);
            } catch (final Exception e) {
            }

        }

    }

    @Override
    public ProcessBpmnDTO details(final String id) {

        final ProcessBpmnDTO dto = new ProcessBpmnDTO();

        final ProcessDefinition model = this.repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(id).latestVersion().singleResult();

        if (null == model) {
            throw new UnitedException(BizcoreActivitiConstants.PROCESS_EXIST);
        }

        final InputStream inputStream =
                this.repositoryService.getResourceAsStream(model.getDeploymentId(),
                        model.getResourceName());

        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int i = -1;
        try {
            while ((i = inputStream.read()) != -1) {
                baos.write(i);
            }
            dto.setXmlContent(baos.toString());
            dto.setName(model.getName());
            dto.setCategory(model.getCategory());
        } catch (final IOException e) {
            throw new UnitedException("工作流xml文件读取失败", e.getMessage());
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
        return dto;
    }

    @Override
    public ProcessNodeDTO getStartForm(final String type) {

        ProcessNodeDTO result = null;

        final ProcessDefinition check = this.repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(type).latestVersion().singleResult();

        if (check == null) {
            throw new UnitedException(BizcoreActivitiConstants.PROCESS_EXIST);
        }

        final BpmnModel bpmnModel =
                this.repositoryService.getBpmnModel(check.getId());

        if (bpmnModel != null) {
            final Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();

            if (CollectionUtils.isNotEmpty(flowElements)) {
                final StartEvent element =
                        flowElements.stream().filter(k -> k instanceof StartEvent).map(m -> (StartEvent) m)
                                .collect(Collectors.toList()).get(0);

                final SequenceFlow outgoing = element.getOutgoingFlows().get(0);

                result = this.processNodeConverter.toDTO(outgoing.getTargetFlowElement());
            }
        }

        return result;
    }

    @Override
    public void upload(final String code, final InputStream file) {
        final String xml = IOUtils.toString(file);
        final String id = this.getProcessIdOrName(xml, "id", 4);
        final String name = this.getProcessIdOrName(xml, "name", 6);
        if (!code.equals(id)) {
            throw new UnitedException("上传文件的流程定义Id(" + id + ")必须和当前工单编码(" + code + ")一致");
        }

        final ProcessBpmnDTO dto = new ProcessBpmnDTO();
        dto.setName(name + ".bpmn");
        dto.setXmlContent(xml);
        this.create(dto);

    }

    private String getProcessIdOrName(final String xml, final String type, final int len) {
        final int start = xml.indexOf("process");
        String filename = xml.substring(start, xml.indexOf(">", start));
        if (filename.indexOf(type) == -1) {
            return null;
        }
        filename = filename.substring(filename.indexOf(type) + len);
        filename = filename.substring(0, filename.indexOf("\""));
        return filename;
    }

}
